package com.cmc.cloud.cmclink.doc.service.impl.special;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmc.cloud.cmclink.doc.api.document.emums.SepcialDGProhibitedListTypeEnum;
import com.cmc.cloud.cmclink.doc.api.document.emums.SpecialEnum;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.SpecialCargoApplyErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.mapper.*;
import com.cmc.cloud.cmclink.doc.rpcservice.VoyScheduleService;
import com.cmc.cloud.cmclink.doc.service.DgProhibitedService;
import com.cmc.cloud.cmclink.doc.service.special.SpecialCargoValidateService;
import com.cmc.cloud.cmclink.svl.api.base.enums.SystemEnums;
import com.cmc.cloud.cmclink.svl.api.base.req.VesselVoyReqDTO;
import com.cmc.cloud.cmclink.svl.api.base.resp.VesselVoyRespDTO;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.ex.Assertion;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.cmc.cloud.cmclink.doc.api.document.emums.SepcialDGProhibitedCodeTypeEnum.*;
import static com.cmc.cloud.cmclink.doc.service.impl.SpecialCargoDgServiceImpl.IMO_CLASS_3;
import static com.cmc.cloud.cmclink.doc.service.impl.SpecialCargoDgServiceImpl.SUB_RISK_3;

/**
 * DG类型货物校验器
 *
 * @author: 申天青
 * @date: 2025/8/4
 */
@Service
public class DgCargoValidateServiceImpl implements SpecialCargoValidateService {
    @Resource
    private SpecialCargoDgMapper specialCargoDgMapper;
    @Resource
    private SpecialCargoDgPackingMapper specialCargoDgPackingMapper;
    @Resource
    private SpecialCargoCasMapper specialCargoCasMapper;
    @Resource
    private SuperWhitelistDgMapper superWhitelistDgMapper;
    @Resource
    private CargoCntrTypeRelationMapper cargoCntrTypeRelationMapper;
    @Resource
    private DgCommodityMapper dgCommodityMapper;
    @Resource
    private AttachmentMapper attachmentMapper;
    @Resource
    private AttachmentTypeMapper attachmentTypeMapper;
    @Resource
    private DgProhibitedService dgProhibitedService;
    @Resource
    private VoyScheduleService voyScheduleService;

    private static final String UN_NO_SPECIAL_CASE = "1950";
    private static final BigDecimal FLASH_POINT_THRESHOLD = BigDecimal.valueOf(-18);
    private static final String CONTAINER_TYPE_TANK = "TK";
    private static final String PORT_TYPE_TRANSIT = "T";
    private static final String PORT_TYPE_POL = "L";
    private static final String PORT_TYPE_POD = "D";

    @Override
    public void validate(Long applyId, SpecialCargoApplyDO applyDO, List<SpecialCargoCntrDO> cntrDOList, List<SpecialCargoVoyageDO> specialCargoVoyageDos) {
        List<SpecialCargoDgDO> dgDOList = specialCargoDgMapper.selectList(new LambdaQueryWrapper<SpecialCargoDgDO>().eq(SpecialCargoDgDO::getApplyId, applyId));
        if (CollectionUtil.isEmpty(dgDOList)) {
            throw ServiceExceptionUtil.exception(SpecialCargoApplyErrorCodeConstants.SPECIAL_NO_DG_ERROR);
        }
        boolean dgPra = Boolean.FALSE;
        SuperWhitelistDgDO superWhitelistDgDo = null;
        if (StringUtils.isNotEmpty(applyDO.getPraNo())) {
            List<String> pols = specialCargoVoyageDos.stream().map(SpecialCargoVoyageDO::getPolCode).collect(Collectors.toList());
            List<String> pods = specialCargoVoyageDos.stream().map(SpecialCargoVoyageDO::getPodCode).collect(Collectors.toList());
            List<String> vessels = specialCargoVoyageDos.stream().map(SpecialCargoVoyageDO::getVesselCode).collect(Collectors.toList());
            List<String> laneCode = specialCargoVoyageDos.stream().map(SpecialCargoVoyageDO::getLaneCode).collect(Collectors.toList());
            String porCode = applyDO.getPorCode();
            String delCode = applyDO.getDelCode();
            LambdaQueryWrapperX<SuperWhitelistDgDO> queryWrapperX = new LambdaQueryWrapperX<SuperWhitelistDgDO>()
                    .eqIfPresent(SuperWhitelistDgDO::getSpecialCargoType, SpecialEnum.DG.getValue())
                    .eqIfPresent(SuperWhitelistDgDO::getPraNo, applyDO.getPraNo())
                    .likeIfPresent(SuperWhitelistDgDO::getPors, porCode)
                    .likeIfPresent(SuperWhitelistDgDO::getDels, delCode);
            superWhitelistDgDo = superWhitelistDgMapper.selectOne(queryWrapperX);
            if (null != superWhitelistDgDo) {
                List<String> praPols = Arrays.asList(superWhitelistDgDo.getPols().split(","));
                List<String> praPods = Arrays.asList(superWhitelistDgDo.getPods().split(","));
                List<String> vesselCodes = Arrays.asList(superWhitelistDgDo.getVesselCodes().split(","));
                List<String> laneCodes = Arrays.asList(superWhitelistDgDo.getLaneCodes().split(","));
                boolean containPol = pols.stream().anyMatch(praPols::contains);
                boolean containPod = pods.stream().anyMatch(praPods::contains);
                boolean containVessel = vessels.stream().anyMatch(vesselCodes::contains);
                boolean containLaneCode = laneCode.stream().anyMatch(laneCodes::contains);
                if (containPol && containPod && containVessel && containLaneCode) {
                    dgPra = Boolean.TRUE;
                }
            }
        }
        for (SpecialCargoDgDO dg : dgDOList) {
            if (dgPra) {
                if (dg.getUnNo().equals(superWhitelistDgDo.getUnNo()) && dg.getImoClass().equals(superWhitelistDgDo.getImoClass()) && dg.getPackingGroup().equals(superWhitelistDgDo.getPackingGroup())) {
                    continue;
                }
            }
            List<SpecialCargoDgPackingDO> packingDos = specialCargoDgPackingMapper.selectList(new LambdaQueryWrapper<SpecialCargoDgPackingDO>().eq(SpecialCargoDgPackingDO::getDgId, dg.getId()));
            if (CollectionUtil.isEmpty(packingDos)) {
                throw ServiceExceptionUtil.exception(SpecialCargoApplyErrorCodeConstants.SPECIAL_PACKING_DG_ERROR);
            }
            List<SpecialCargoCasDO> casDos = specialCargoCasMapper.selectList(new LambdaQueryWrapper<SpecialCargoCasDO>().eq(SpecialCargoCasDO::getSpecialCargoDgId, dg.getId()));
            if (applyDO.getTransitCnsha() && CollectionUtil.isEmpty(casDos)) {
                throw ServiceExceptionUtil.exception(SpecialCargoApplyErrorCodeConstants.SPECIAL_CAS_DG_ERROR);
            }
            //IMO Class危险品等级	系统按照UN NO.自动显示，（仅UN NO.为1950的，可以人为调整）
            if (StringUtils.isNotEmpty(dg.getImoClass()) && !UN_NO_SPECIAL_CASE.equals(dg.getUnNo())) {
                // 校验IMO Class是否在数据库中存在
                LambdaQueryWrapperX<DgCommodityDO> queryWrapperX = new LambdaQueryWrapperX<>();
                queryWrapperX.eq(DgCommodityDO::getUnNo, dg.getUnNo());
                queryWrapperX.eq(DgCommodityDO::getImoClass, dg.getImoClass());

                if (!dgCommodityMapper.exists(queryWrapperX)) {
                    throw ServiceExceptionUtil.exception(SpecialCargoApplyErrorCodeConstants.SPECIAL_IMO_CLASS_ERROR);
                }
            }

            //Segregation Group	IMDG表中NOS字段为Y时，必须填写
            if (dgCommodityMapper.exists(new LambdaQueryWrapperX<DgCommodityDO>()
                    .eq(DgCommodityDO::getUnNo, dg.getUnNo())
                    .eq(DgCommodityDO::getImoClass, dg.getImoClass())
                    .eq(DgCommodityDO::getNos, "Y"))) {
                if (StringUtils.isEmpty(dg.getSegregationGroup())) {
                    throw ServiceExceptionUtil.exception(SpecialCargoApplyErrorCodeConstants.SPECIAL_SEGREGATION_GROUP_ERROR);
                }
            }
            //闪点（℃）	Flash Point(℃)	文本框		IMO Class 为 3 类时，必填			单位是摄氏度
            //客户提交申请时，校核CLASS，是CLASS 3的，本项目为必填项，(IMDG表中FP存在数据时，必须校验表中数据是否大于-18，否则不得提交。)
            if ((dg.getImoClass() != null && dg.getImoClass().startsWith(IMO_CLASS_3)) || (dg.getSubsidiaryRisk() != null && dg.getSubsidiaryRisk().startsWith(SUB_RISK_3))) {
                if (dg.getFlashPoint() == null) {
                    throw ServiceExceptionUtil.exception(SpecialCargoApplyErrorCodeConstants.SPECIAL_FLASH_POINT_ERROR);
                } else {
                    if (dgCommodityMapper.exists(new LambdaQueryWrapperX<DgCommodityDO>()
                            .eq(DgCommodityDO::getUnNo, dg.getUnNo())
                            .eq(DgCommodityDO::getImoClass, dg.getImoClass())
                            .isNotNull(DgCommodityDO::getFlashPoint))) {
                        BigDecimal flashPoint = dg.getFlashPoint();
                        if (flashPoint != null && flashPoint.compareTo(FLASH_POINT_THRESHOLD) < 0) {
                            throw ServiceExceptionUtil.exception(SpecialCargoApplyErrorCodeConstants.SPECIAL_FLASH_POINT_ERROR);
                        }
                    }
                }
            }
        }
        cntrDOList.forEach(cntr -> {
            List<CargoCntrTypeRelationDO> cntrDgRelations = cargoCntrTypeRelationMapper.selectList(new LambdaQueryWrapperX<CargoCntrTypeRelationDO>().eq(CargoCntrTypeRelationDO::getCntrId, cntr.getId()));
            if (CollectionUtil.isEmpty(cntrDgRelations)) {
                throw ServiceExceptionUtil.exception(SpecialCargoApplyErrorCodeConstants.SPECIAL_CNTR_DG_ERROR);
            }
            //DG页面的附件上传，箱型为TK时，TK 箱检验证书为必填，非TK箱型不必填
            if (CONTAINER_TYPE_TANK.equals(cntr.getCntrType())) {

                if (!attachmentMapper.exists(new LambdaQueryWrapperX<AttachmentDO>()
                        .eq(AttachmentDO::getReferenceId, applyId)
                        //@see 单证\基础数据\附件类型维护
                        .eq(AttachmentDO::getAttachTypeCode, "DG396"))
                ) {
                    throw ServiceExceptionUtil.exception(SpecialCargoApplyErrorCodeConstants.SPECIAL_TK_ATTACHMENT_ERROR);
                }

            }
        });
        // 添加禁接清单校验逻辑
        DgProhibitedDO dgProhibitedDO = checkProhibitedList(specialCargoVoyageDos, dgDOList, cntrDOList);
        if (null != dgProhibitedDO) {
            Assertion.assertTrue(false).raise0(SpecialCargoApplyErrorCodeConstants.SPECIAL_APPLY_PROHIBITED_LIST.getCode(),
                    StringUtils.isEmpty(dgProhibitedDO.getRemark()) ? StringUtils.EMPTY : dgProhibitedDO.getRemark() + ",", dgProhibitedDO.getCodeType(), dgProhibitedDO.getCodeValue());
        }
    }


    /**
     * 一个特种货运单包含多个DG货物，要检查每个DG货物在运程中的航次中的船名、港口（装、卸、中转）是否在名单中
     * 检查是否命中禁接清单
     * 白名单优先级高于黑名单，如果命中白名单则不检查黑名单
     *
     * @return 如果命中禁接清单返回true，否则返回false
     */
    public DgProhibitedDO checkProhibitedList(List<SpecialCargoVoyageDO> specialCargoVoyageDos, List<SpecialCargoDgDO> dgList,
                                              List<SpecialCargoCntrDO> cntrDOList) {
        // 如果没有DG货物，不进行禁接检查
        if (CollectionUtils.isEmpty(dgList)) {
            return null;
        }
        // 对每个DG货物进行检查
        for (SpecialCargoCntrDO cntrDO : cntrDOList) {
            for (SpecialCargoDgDO dg : dgList) {
                for (SpecialCargoVoyageDO voyageDo : specialCargoVoyageDos) {
                    // 白名单优先级高于黑名单，如果命中白名单则不检查黑名单
                    //航线
                    DgProhibitedDO inWhiteLanList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Whitelist.name(), LaneCode.name(), voyageDo.getLaneCode(), dg, cntrDO);
                    if (null != inWhiteLanList) {
                        continue;
                    } else {
                        // 如果不在白名单中，则检查是否在黑名单中
                        DgProhibitedDO inBlackLanList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Blacklist.name(), LaneCode.name(), voyageDo.getLaneCode(), dg, cntrDO);
                        if (null != inBlackLanList) {
                            return inBlackLanList;
                        }
                    }
                    //合作方
                    DgProhibitedDO inWhitePartnerList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Whitelist.name(), ParterCode.name(), voyageDo.getCarrier(), dg, cntrDO);
                    if (null != inWhitePartnerList) {
                        continue;
                    } else {
                        // 如果不在白名单中，则检查是否在黑名单中
                        DgProhibitedDO inBlackPartnerList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Blacklist.name(), ParterCode.name(), voyageDo.getCarrier(), dg, cntrDO);
                        if (null != inBlackPartnerList) {
                            return inBlackPartnerList;
                        }
                    }
                    //船名
                    DgProhibitedDO inWhiteVesselList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Whitelist.name(), VesselCode.name(), voyageDo.getVesselCode(), dg, cntrDO);
                    if (null != inWhiteVesselList) {
                        continue;
                    } else {
                        // 如果不在白名单中，则检查是否在黑名单中
                        DgProhibitedDO inBlackVesselList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Blacklist.name(), VesselCode.name(), voyageDo.getVesselCode(), dg, cntrDO);
                        if (null != inBlackVesselList) {
                            return inBlackVesselList;
                        }
                    }
                    //装货港
                    DgProhibitedDO inWhitePolList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Whitelist.name(), PortCode.name(), voyageDo.getPolCode(), dg, cntrDO);
                    if (null != inWhitePolList) {
                        String portType = inWhitePolList.getPortType();
                        if (StringUtils.isEmpty(portType) || (StringUtils.isNotEmpty(portType) && portType.contains(PORT_TYPE_POL))) {
                            continue;
                        }
                        if (StringUtils.isNotEmpty(portType) && portType.contains(PORT_TYPE_TRANSIT)) {
                            Boolean transitPort = checkTransitPort(voyageDo.getVesselCode(), voyageDo.getVoyageEx(), voyageDo.getPolCode());
                            if (transitPort) {
                                continue;
                            }
                        }
                    } else {
                        // 如果不在白名单中，则检查是否在黑名单中
                        DgProhibitedDO inBlackPolList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Blacklist.name(), PortCode.name(), voyageDo.getPolCode(), dg, cntrDO);
                        if (null != inBlackPolList) {
                            String portType = inBlackPolList.getPortType();
                            if (StringUtils.isEmpty(portType) || (StringUtils.isNotEmpty(portType) && portType.contains(PORT_TYPE_POL))) {
                                return inBlackPolList;
                            }
                            if (StringUtils.isNotEmpty(portType) && portType.contains(PORT_TYPE_TRANSIT)) {
                                Boolean transitPort = checkTransitPort(voyageDo.getVesselCode(), voyageDo.getVoyageEx(), voyageDo.getPolCode());
                                if (transitPort) {
                                    return inBlackPolList;
                                }
                            }
                        }
                    }
                    //卸货港
                    DgProhibitedDO inWhitePodList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Whitelist.name(), PortCode.name(), voyageDo.getPodCode(), dg, cntrDO);
                    if (null != inWhitePodList) {
                        String portType = inWhitePodList.getPortType();
                        if (StringUtils.isEmpty(portType) || (StringUtils.isNotEmpty(portType) && portType.contains(PORT_TYPE_POD))) {
                            continue;
                        }
                        if (StringUtils.isNotEmpty(portType) && portType.contains(PORT_TYPE_TRANSIT)) {
                            Boolean transitPort = checkTransitPort(voyageDo.getVesselCode(), voyageDo.getVoyageEx(), voyageDo.getPodCode());
                            if (transitPort) {
                                continue;
                            }
                        }
                    } else {
                        // 如果不在白名单中，则检查是否在黑名单中
                        DgProhibitedDO inBlackPodList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Blacklist.name(), PortCode.name(), voyageDo.getPodCode(), dg, cntrDO);
                        if (null != inBlackPodList) {
                            String portType = inBlackPodList.getPortType();
                            if (StringUtils.isEmpty(portType) || (StringUtils.isNotEmpty(portType) && portType.contains(PORT_TYPE_POD))) {
                                return inBlackPodList;
                            }
                            if (StringUtils.isNotEmpty(portType) && portType.contains(PORT_TYPE_TRANSIT)) {
                                Boolean transitPort = checkTransitPort(voyageDo.getVesselCode(), voyageDo.getVoyageEx(), voyageDo.getPodCode());
                                if (transitPort) {
                                    return inBlackPodList;
                                }
                            }
                        }
                    }
                    //过境
                    Set<String> transitPort = getTransitPort(voyageDo.getVesselCode(), voyageDo.getVoyageEx());
                    for (String port : transitPort) {
                        DgProhibitedDO inWhiteTransitList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Whitelist.name(), PortCode.name(), port, dg, cntrDO);
                        if (null != inWhiteTransitList) {
                            String portType = inWhiteTransitList.getPortType();
                            if (StringUtils.isNotEmpty(portType) && portType.contains(PORT_TYPE_TRANSIT)) {
                                continue;
                            }
                            DgProhibitedDO inBlackTransitList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Blacklist.name(), PortCode.name(), port, dg, cntrDO);
                            if(null!=inBlackTransitList){
                                String portTypeBlack = inBlackTransitList.getPortType();
                                if (StringUtils.isNotEmpty(portTypeBlack) && portTypeBlack.contains(PORT_TYPE_TRANSIT)) {
                                    return inBlackTransitList;
                                }
                            }
                        }else {
                            DgProhibitedDO inBlackTransitList = dgProhibitedService.isInList(SepcialDGProhibitedListTypeEnum.Blacklist.name(), PortCode.name(), port, dg, cntrDO);
                            if(null!=inBlackTransitList){
                                String portType = inBlackTransitList.getPortType();
                                if (StringUtils.isNotEmpty(portType) && portType.contains(PORT_TYPE_TRANSIT)) {
                                    return inBlackTransitList;
                                }
                            }
                        }
                    }


                }
            }
        }
        return null;
    }

    public Boolean checkTransitPort(String vesselCode, String voyageEx, String portCode) {
        VesselVoyReqDTO reqDto = new VesselVoyReqDTO();
        reqDto.setVesselCodeList(Collections.singletonList(vesselCode));
        reqDto.setVoy(voyageEx);
        reqDto.setSystemNumber(SystemEnums.IBS.getSystemNumber());
        List<VesselVoyRespDTO> vesselVoyResp = voyScheduleService.listVesselVoy(reqDto);
        if (CollectionUtil.isNotEmpty(vesselVoyResp)) {
            return vesselVoyResp.stream()
                    .anyMatch(dto -> portCode.equals(dto.getPortCode()));
        }
        return false;
    }

    public Set<String> getTransitPort(String vesselCode, String voyageEx) {
        VesselVoyReqDTO reqDto = new VesselVoyReqDTO();
        reqDto.setVesselCodeList(Collections.singletonList(vesselCode));
        reqDto.setVoy(voyageEx);
        reqDto.setSystemNumber(SystemEnums.IBS.getSystemNumber());
        List<VesselVoyRespDTO> vesselVoyResp = voyScheduleService.listVesselVoy(reqDto);
        if (CollectionUtil.isNotEmpty(vesselVoyResp)) {
            return vesselVoyResp.stream().map(VesselVoyRespDTO::getPortCode).collect(Collectors.toSet());
        }
        return Collections.emptySet();
    }

}
